rns.moscow 🟥 [git]
htdocs/api/track.php main (300ad1dc) Text, 2.54 KB
Tff7b72<?php
T8b949e// Records one anonymous "device flashed" event. Called by the flasher UI
T8b949e// right after a flash completes successfully.
Tff7b72require Tff7b72__DIR__ Tff7b72. Ta5d6ff'/db.php'Tb4b4b4;
Tffa657headerTb4b4b4(Ta5d6ff'Content-Type: application/json'Tb4b4b4);
Tff7b72if Tb4b4b4(Te6edf3$_SERVERTb4b4b4[Ta5d6ff'REQUEST_METHOD'Tb4b4b4] Tff7b72!== Ta5d6ff'POST'Tb4b4b4) Tb4b4b4{
Tffa657http_response_codeTb4b4b4(T79c0ff405Tb4b4b4);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'error' Tff7b72=> Ta5d6ff'method not allowed'Tb4b4b4]);
Tff7b72exitTb4b4b4;
Tb4b4b4}
T8b949e// Reject cross-site/non-browser POSTs: a same-origin fetch() always sends
T8b949e// either Origin or Referer pointing back at this host. Plain scripts/bots
T8b949e// hitting the endpoint directly typically send neither.
Tff7b72function Td2a8ffrequest_host_okTb4b4b4()Tff7b72: Te6edf3bool Tb4b4b4{
Te6edf3$host Tff7b72= Te6edf3$_SERVERTb4b4b4[Ta5d6ff'HTTP_HOST'Tb4b4b4] Tff7b72?Tff7b72? Ta5d6ff''Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3$host Tff7b72=== Ta5d6ff''Tb4b4b4) Tff7b72return Tff7b72falseTb4b4b4;
Te6edf3$origin Tff7b72= Te6edf3$_SERVERTb4b4b4[Ta5d6ff'HTTP_ORIGIN'Tb4b4b4] Tff7b72?Tff7b72? Ta5d6ff''Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3$origin Tff7b72!== Ta5d6ff''Tb4b4b4) Tb4b4b4{
Tff7b72return Tffa657parse_urlTb4b4b4(Te6edf3$originTb4b4b4, Te6edf3PHP_URL_HOSTTb4b4b4) Tff7b72=== Te6edf3$hostTb4b4b4;
Tb4b4b4}
Te6edf3$referer Tff7b72= Te6edf3$_SERVERTb4b4b4[Ta5d6ff'HTTP_REFERER'Tb4b4b4] Tff7b72?Tff7b72? Ta5d6ff''Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3$referer Tff7b72!== Ta5d6ff''Tb4b4b4) Tb4b4b4{
Tff7b72return Tffa657parse_urlTb4b4b4(Te6edf3$refererTb4b4b4, Te6edf3PHP_URL_HOSTTb4b4b4) Tff7b72=== Te6edf3$hostTb4b4b4;
Tb4b4b4}
Tff7b72return Tff7b72falseTb4b4b4;
Tb4b4b4}
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3request_host_okTb4b4b4()) Tb4b4b4{
Tffa657http_response_codeTb4b4b4(T79c0ff403Tb4b4b4);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'error' Tff7b72=> Ta5d6ff'forbidden'Tb4b4b4]);
Tff7b72exitTb4b4b4;
Tb4b4b4}
Te6edf3$body Tff7b72= Tffa657json_decodeTb4b4b4(Tffa657file_get_contentsTb4b4b4(Ta5d6ff'php://input'Tb4b4b4), Tff7b72trueTb4b4b4);
Tff7b72if Tb4b4b4(Tff7b72!Tffa657is_arrayTb4b4b4(Te6edf3$bodyTb4b4b4)) Tb4b4b4{
Tffa657http_response_codeTb4b4b4(T79c0ff400Tb4b4b4);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'error' Tff7b72=> Ta5d6ff'invalid json'Tb4b4b4]);
Tff7b72exitTb4b4b4;
Tb4b4b4}
Tff7b72function Td2a8ffclean_fieldTb4b4b4(Te6edf3$vTb4b4b4, Te6edf3int Te6edf3$maxLenTb4b4b4)Tff7b72: Tff7b72?Te6edf3string Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Tffa657is_stringTb4b4b4(Te6edf3$vTb4b4b4)) Tff7b72return Tff7b72nullTb4b4b4;
Te6edf3$v Tff7b72= Tffa657trimTb4b4b4(Tffa657preg_replaceTb4b4b4(Ta5d6ff'/[\x00-\x1F\x7F]/u'Tb4b4b4, Ta5d6ff''Tb4b4b4, Te6edf3$vTb4b4b4));
Tff7b72if Tb4b4b4(Te6edf3$v Tff7b72=== Ta5d6ff'' Tff7b72|| Tffa657mb_strlenTb4b4b4(Te6edf3$vTb4b4b4) Tff7b72> Te6edf3$maxLenTb4b4b4) Tff7b72return Tff7b72nullTb4b4b4;
Tff7b72return Te6edf3$vTb4b4b4;
Tb4b4b4}
Te6edf3$device Tff7b72= Te6edf3clean_fieldTb4b4b4(Te6edf3$bodyTb4b4b4[Ta5d6ff'device'Tb4b4b4] Tff7b72?Tff7b72? Tff7b72nullTb4b4b4, T79c0ff100Tb4b4b4);
Te6edf3$variant Tff7b72= Te6edf3clean_fieldTb4b4b4(Te6edf3$bodyTb4b4b4[Ta5d6ff'variant'Tb4b4b4] Tff7b72?Tff7b72? Tff7b72nullTb4b4b4, T79c0ff150Tb4b4b4);
Te6edf3$platform Tff7b72= Te6edf3$bodyTb4b4b4[Ta5d6ff'platform'Tb4b4b4] Tff7b72?Tff7b72? Tff7b72nullTb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3$device Tff7b72=== Tff7b72null Tff7b72|| Te6edf3$variant Tff7b72=== Tff7b72null Tff7b72|| Tff7b72!Tffa657in_arrayTb4b4b4(Te6edf3$platformTb4b4b4, Tb4b4b4[Ta5d6ff'esp32'Tb4b4b4, Ta5d6ff'nrf52'Tb4b4b4], Tff7b72trueTb4b4b4)) Tb4b4b4{
Tffa657http_response_codeTb4b4b4(T79c0ff400Tb4b4b4);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'error' Tff7b72=> Ta5d6ff'invalid fields'Tb4b4b4]);
Tff7b72exitTb4b4b4;
Tb4b4b4}
Te6edf3$pdo Tff7b72= Te6edf3stats_dbTb4b4b4();
T8b949e// Abuse guard: cap at 20 tracked flashes per IP per 10 minutes. Only the
T8b949e// hash is ever stored — the raw IP is never persisted.
Te6edf3$ipHash Tff7b72= Tffa657hashTb4b4b4(Ta5d6ff'sha256'Tb4b4b4, Ta5d6ff'rnstats-' Tff7b72. Tb4b4b4(Te6edf3$_SERVERTb4b4b4[Ta5d6ff'REMOTE_ADDR'Tb4b4b4] Tff7b72?Tff7b72? Ta5d6ff''Tb4b4b4));
Te6edf3$since Tff7b72= Tffa657timeTb4b4b4() Tff7b72- T79c0ff600Tb4b4b4;
Te6edf3$rateStmt Tff7b72= Te6edf3$pdoTff7b72->Te6edf3prepareTb4b4b4(Ta5d6ff'SELECT COUNT(*) FROM flashes WHERE ip_hash = ? AND ts > ?'Tb4b4b4);
Te6edf3$rateStmtTff7b72->Te6edf3executeTb4b4b4([Te6edf3$ipHashTb4b4b4, Te6edf3$sinceTb4b4b4]);
Tff7b72if Tb4b4b4((Te6edf3intTb4b4b4)Te6edf3$rateStmtTff7b72->Te6edf3fetchColumnTb4b4b4() Tff7b72>= T79c0ff20Tb4b4b4) Tb4b4b4{
Tffa657http_response_codeTb4b4b4(T79c0ff429Tb4b4b4);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'error' Tff7b72=> Ta5d6ff'rate limited'Tb4b4b4]);
Tff7b72exitTb4b4b4;
Tb4b4b4}
Te6edf3$stmt Tff7b72= Te6edf3$pdoTff7b72->Te6edf3prepareTb4b4b4(Ta5d6ff'INSERT INTO flashes (device, variant, platform, ip_hash, ts) VALUES (?, ?, ?, ?, ?)'Tb4b4b4);
Te6edf3$stmtTff7b72->Te6edf3executeTb4b4b4([Te6edf3$deviceTb4b4b4, Te6edf3$variantTb4b4b4, Te6edf3$platformTb4b4b4, Te6edf3$ipHashTb4b4b4, Tffa657timeTb4b4b4()]);
Tff7b72echo Tffa657json_encodeTb4b4b4([Ta5d6ff'ok' Tff7b72=> Tff7b72trueTb4b4b4]);
Served by rngit 1.4.2 - Generated in 0.04s